package asu.util
{
	import flash.net.registerClassAlias;
	import flash.utils.ByteArray;
	import flash.utils.describeType;
	import flash.utils.getQualifiedClassName;
	
	public class ObjUtil
	{
		// not verified to work, pasted here
		public static function clone( original:*, withClass:Class, withClassPath:String ):*
		{
			registerClassAlias( withClassPath, withClass );
			var ba:ByteArray = new ByteArray();
			ba.writeObject( original );
			ba.position = 0;
			var clone:* = (ba.readObject()) as withClass;
			return clone;
		}
		
		public static function merge( ...rest ):Object
		{
			var obj:Object = {};
			
			for each( var o:Object in rest )
			{
				for( var prop:String in o )
				{
					obj[ prop ] = o[ prop ];
				}
			}
			
			return obj;
		}
		
		public static function getClassName( o:Object ):String
		{
			return getQualifiedClassName( o ).split( '::' )[1];
		}
		
		public static function toString( target:Object, properties:Array=null ):String
		{
			var str:String;
			
			if( properties )
			{
				str = "["+getClassName( target );
				
				for each( var p:String in properties )
				{
					if( target.hasOwnProperty( p ) )
					{
						str += " "+p;
						
						if( target[p] is Array && target[p] )
						{
							str += "("+String( target[p].length )+")";
						}
						
						str += "='"+String(target[p])+"'";
					}
				}
				
				str += "]";
			}
			else
			{
				str = "["+String(target);
				for( var i:String in target )
				{
					str  += "\n\t"+i+" : "+String(target[ i ])+"\n";
				}
				str += "]";
			}
			
			return str;
		}
		
		
		
		public static function setParams( main:Object, paramObj:Object ):Object
		{
			//trace( "ObjUtil::setParams", main, paramObj );
			
			if( paramObj )
			{
				for( var k:String in paramObj )
				{
					//trace( k );
					
					if( main.hasOwnProperty( k ) && paramObj[k] != null )
					{
						main[k] = paramObj[k];
					}
						
				}
			}
			
			return main;
		}
		
		
		public static function setProperties( main:Object, properties:Object, omitProps:Array=null, omitVals:Array=null, extra:Array=null ):Object
		{
			var returnExtra:Object = extra ? {} : null;
			
			if( !omitProps ) omitProps = [];
			if( !omitVals ) omitVals = [];
			
			for(var property:String in properties)
			{
				if(main.hasOwnProperty(property) && omitVals.indexOf(properties[property]) < 0 && omitProps.indexOf(property) < 0 )
				{
					main[property] = properties[property];
				}
				else if( extra )
				{
					if( extra.indexOf(property) > -1 )
					{
						returnExtra[property] = properties[property];
					}
				}
			}
			
			return returnExtra;
		}
		
		
		
		
		
		public static function traceProperties( o:Object ):void
		{
			trace( "ObjUtil::traceProperties", o );
			
			for( var i:String in o )
			{
				trace( i+": "+o[i] );
			}
			
			trace();
		}
		
		public static function getKeys( o:Object ):Array
		{
			var keys:Array = [];
			
			for( var i:String in o )
			{
				keys.push( i );
			}
			
			return keys;
		}
		
		public static function traceNestedObject( obj:Object, inn:String="" ):void
		{
			for( var i:String in obj )
			{
				trace(inn+i);
				
				if( obj[i] != null )
				{
					inn += '-';
					traceNestedObject( obj[i], inn );
					inn = inn.substr(0, inn.length-1 );
				}
			}
		}
		
		public static function traceClassProperties( o:Object ):void
		{
			trace();
			trace( '----------------------------------------------------' );
			trace( "ObjUtil::traceClassProperties for "+getClassName( o ) );
			trace( '----------------------------------------------------' );
			
			var desc:XML= describeType( o );
			
			// public vars
			for each (var v:XML in desc.variable){
				trace(v.@name, o[v.@name]);
			}
			// getters
			for each (v in desc.accessor){
				trace(v.@name, o[v.@name]);
			}
			
			trace( '----------------------------------------------------' );
			trace();
		}
		
		public static function toEnumerableObject( instance:Object ):Object
		{
			var desc:XML= describeType( instance );
			var obj:Object = {};
			
			// public vars
			for each (var v:XML in desc.variable){
				obj[v.@name] = instance[v.@name];
			}
			// getters
			for each (v in desc.accessor){
				obj[v.@name] = instance[v.@name];
			}
			
			return obj;
		}
	}
}